// Package pool 管理用户资源的池子
package pool

import (
	"errors"
	"io"
	"log"
	"sync"
)

// 说明go 1.6之后，自带sync.Pool用来管理资源

// Pool 管理用户资源的池子，可以在多个goroutine之间共享资源
type Pool struct {
	m         sync.Mutex
	resources chan io.Closer
	factory   func() (io.Closer, error)
	closed    bool
}

// ErrPoolClosed 表示(Acquire)了一个已经关闭的池子
var ErrPoolClosed = errors.New("pool has been closed")

// New 创建一个新的池子，这个池子会调用factory创建资源，并可设置资源上限
func New(fn func() (io.Closer, error), size uint) (*Pool, error) {
	if size <= 0 {
		return nil, errors.New("size value too small")
	}

	return &Pool{
		factory:   fn,
		resources: make(chan io.Closer, size),
	}, nil
}

// Acquire 从池子中获取一个资源
func (p *Pool) Acquire() (io.Closer, error) {
	select {
	// 检查是否有空闲资源
	case r, ok := <-p.resources:
		if !ok {
			return nil, ErrPoolClosed
		}
		return r, nil
	//	因为没有空闲资源，所以创建一个新资源
	default:
		if p.factory == nil {
			return nil, ErrPoolClosed
		}
		return p.factory()
	}
}

// Release 将一个使用后的资源放回池子里面
func (p *Pool) Release(r io.Closer) {
	// Secure this operation with the Close operation.
	p.m.Lock()
	defer p.m.Unlock()
	// If the pool is closed, discard the resource.
	if p.closed {
		err := r.Close()
		if err != nil {
			return
		}
		return
	}
	// select能将所有阻塞的资源通过默认分支变成非阻塞的分支
	select {
	// 试图将资源放回资源池
	case p.resources <- r:
		log.Println("Release:", "In Queue")
	// 如果资源池已经满了，就关闭这个资源
	default:
		log.Println("Release:", "Closing")
		err := r.Close()
		if err != nil {
			return
		}
	}
}

// Close 关闭资源池
func (p *Pool) Close() {
	// 保证多线程安全
	p.m.Lock()
	defer p.m.Unlock()

	// 如果资源池已经关闭，就不用再关闭了
	if p.closed {
		return
	}
	// 关闭资源池并释放所有资源
	close(p.resources)

	p.closed = true
	// 通道即使关闭了，也能通过range将通道中的资源全部接收一遍
	for r := range p.resources {
		err := r.Close()
		if err != nil {
			return
		}
	}
}
